/**
 * basic.js
 * For common using methods
 */

"use strict";
/*-----------时间--------*/
import moment from 'moment';
moment.locale('zh-cn');
import crypto from "crypto";

function convertUndefinedToNull(data: undefined | null): number | null {
  if (
    typeof data === "undefined" ||
    data === "undefined" ||
    data === "" ||
    data === null ||
    data === "null"
  ) {
    return null;
  } else {
    return data;
  }
};

function convertStringToFixedLength(hexString: string, wantedLength: number) {
  let loopTimes = wantedLength - hexString.length;
  for (let i = 0; i < loopTimes; i++) {
    hexString = "0" + hexString;
  }
  return hexString.toUpperCase();
};

//Function to format a buffer byte to hex string
function hex2StrAddZero(value: number) {
  var valueHex;
  if (value < 16) {
    valueHex = "0" + value.toString(16).toUpperCase();
  } else {
    valueHex = value.toString(16);
    valueHex = valueHex.toUpperCase();
  }
  return valueHex;
};

//Function to generate a time object from buffer
function hexTime2Str(buf: string) {
  let time;
  let timeStr;
  let timeStamp;
  let year = parseInt(buf[0] + buf[1], 16);
  let month = parseInt(buf[2], 16);
  let day = parseInt(buf[3], 16);
  let hour = parseInt(buf[4], 16);
  let minutes = parseInt(buf[5], 16);
  let second = parseInt(buf[6], 16);

  timeStr =
    convertStringToFixedLength(year.toString(), 4) +
    "-" +
    convertStringToFixedLength(month.toString(), 2) +
    "-" +
    convertStringToFixedLength(day.toString(), 2) +
    " " +
    convertStringToFixedLength(hour.toString(), 2) +
    ":" +
    convertStringToFixedLength(minutes.toString(), 2) +
    ":" +
    convertStringToFixedLength(second.toString(), 2);

  timeStamp = Date.parse(timeStr);

  // if(isNaN(timeStamp))

  //如果要转换的时间非法或与当前时间差值大于一个月则使用当前服务器时间
  if (isNaN(timeStamp) || Math.abs(timeStamp - Date.now()) > 3600 * 24 * 30) {
    timeStamp = Date.now();
  }

  time = {
    timeStr: timeStr,
    timeStamp: timeStamp
  };
  return time;
};
//-------------------------------------------------------------------------------------------------------------//

//Function to generate a time object from buffer 从时间buffer生成时间对象
function timeBuf2TimeStr(buf: any) {
  let time;
  let timeStr;
  let timeStamp;
  let year = parseInt(buf.slice(0, 2).toString("hex"), 16);
  let month = parseInt(buf[2]);
  let day = parseInt(buf[3]);
  let hour = parseInt(buf[4]);
  let minutes = parseInt(buf[5]);
  let second = parseInt(buf[6]);

  let dateObj = new Date(year, month, day, hour, minutes, second);

  timeStr =
    convertStringToFixedLength(year.toString(), 4) +
    "-" +
    convertStringToFixedLength(month.toString(), 2) +
    "-" +
    convertStringToFixedLength(day.toString(), 2) +
    " " +
    convertStringToFixedLength(hour.toString(), 2) +
    ":" +
    convertStringToFixedLength(minutes.toString(), 2) +
    ":" +
    convertStringToFixedLength(second.toString(), 2);

  // timeStr = dateObj.toLocaleString();
  // timeStamp = Date.parse(timeStr);
  timeStamp = dateObj.getTime();

  // if(isNaN(timeStamp))

  //如果要转换的时间非法或与当前时间差值大于一个月则使用当前服务器时间
  if (isNaN(timeStamp) || Math.abs(timeStamp - Date.now()) > 3600 * 24 * 30) {
    timeStamp = Date.now();
  }

  time = {
    timeStr: timeStr,
    timeStamp: timeStamp
  };
  return time;
};
//-------------------------------------------------------------------------------------------------------------//
function nanNumber2Null(number: number) {
  if (isNaN(number)) {
    return null;
  } else {
    return number;
  }
};

//Convert a HEX number string to specific length
function convertHexNumStrToWantedLengthUppercase(hexString: string, wantedLength: number): string {
  let loopTimes = wantedLength - hexString.length;
  for (let i = 0; i < loopTimes; i++) {
    hexString = "0" + hexString;
  }
  return hexString.toUpperCase();
};

//-------------------------------------------------------------------------------------------------------------//

//Generate a current HEX time string
function generateCurrentHexTimeString() {
  let dateObj = new Date();
  let year = dateObj.getFullYear().toString(16);
  let month = (dateObj.getMonth() + 1).toString(16);
  let date = dateObj.getDate().toString(16);
  let hour = dateObj.getHours().toString(16);
  let min = dateObj.getMinutes().toString(16);
  let sec = dateObj.getSeconds().toString(16);

  if (year.length < 4) {
    for (let i = 0; i < 4 - year.length; i++) {
      year = "0" + year;
    }
  }

  if (month.length < 2) {
    for (let i = 0; i < 2 - month.length; i++) {
      month = "0" + month;
    }
  }

  if (date.length < 2) {
    for (let i = 0; i < 2 - date.length; i++) {
      date = "0" + date;
    }
  }

  if (hour.length < 2) {
    for (let i = 0; i < 2 - hour.length; i++) {
      hour = "0" + hour;
    }
  }

  if (min.length < 2) {
    for (let i = 0; i < 2 - min.length; i++) {
      min = "0" + min;
    }
  }

  if (sec.length < 2) {
    for (let i = 0; i < 2 - sec.length; i++) {
      sec = "0" + sec;
    }
  }

  return year + month + date + hour + min + sec;
};
//-------------------------------------------------------------------------------------------------------------//

//Function to generate time object from current time
function generateCurrentTimeObj() {
  let dateObj = new Date();
  let year = dateObj.getFullYear();
  let month = dateObj.getMonth() + 1;
  let date = dateObj.getDate();
  let hour = dateObj.getHours();
  let min = dateObj.getMinutes();
  let sec = dateObj.getSeconds();

  let timeStr =
    convertStringToFixedLength(year.toString(), 4) +
    "-" +
    convertStringToFixedLength(month.toString(), 2) +
    "-" +
    convertStringToFixedLength(date.toString(), 2) +
    " " +
    convertStringToFixedLength(hour.toString(), 2) +
    ":" +
    convertStringToFixedLength(min.toString(), 2) +
    ":" +
    convertStringToFixedLength(sec.toString(), 2);
  let timeStamp = Date.parse(timeStr);

  let time = {
    timeStr: timeStr,
    timeStamp: timeStamp
  };
  console.log(`${new Date().toLocaleString()}`,
    `⊙０⊙⊙０⊙⊙０⊙⊙０⊙⊙０⊙⊙０⊙⊙０⊙⊙０⊙⊙０⊙⊙０⊙⊙０⊙timeStr is: ${timeStr}⊙０⊙⊙０⊙⊙０⊙⊙０⊙`
  );
  return time;
};

//Function to generate hour-level timeStamp from a second-level timeStamp
function getAHourTimestamp(timeStamp: string | number | Date) {
  let date = new Date(timeStamp);
  let year = date.getFullYear();
  let month = date.getMonth() + 1;
  let day = date.getDate();
  let hour = date.getHours();
  let min = date.getMinutes();
  let sec = date.getSeconds();
  let hourTimestamp = Date.parse(
    year + "-" + month + "-" + day + " " + hour + ":00:00"
  );
  return {
    hourStr: year + "-" + (month + 1) + "-" + day + " " + hour + ":00:00",
    hourStamp: hourTimestamp,
    minutes: min,
    seconds: sec
  };
};

//**生成一个字节的累加校验码 data是个数组*/
function generateOneByteSumCode(data: number[]) {
  let accumulationValue = 0;
  let accumulationCheckValue;
  let dataLength = data.length;
  for (let i = 0; i < dataLength; i++) {
    accumulationValue += data[i]; //挨个从中间数组中取出来进行累加
    // console.log(`${new Date().toLocaleString()}`,`value is : ${data[i]}, accumulationValue is: ${accumulationValue}`);
  }
  accumulationCheckValue = accumulationValue.toString(16).slice(-2);

  return accumulationCheckValue;
};
//-----------------------------------------------------------------------//

//-----------------------------------------------------------------------//
//**生成CRC8校验 返回一个16进制字符串*/
//**生成CRC8校验 返回一个16进制字符串*/
function generateCRC8Code(crc8Buff: Buffer) {
  let data = new Array();; //因为node.js中直接使用readUInt8()读取buffer中的8个字节，会导致被累加类型也转为8个字节的整形(最大256)，在累加时会提前溢出，所以先建个中间数组存起来再加
  let dataLength = crc8Buff.length;
  for (let i = 0; i < dataLength; i++) {
    data.push(crc8Buff.readUInt8(i)); //将tempBuf[3]至tempBuf[11]分别按8位无符号取出放入中间数组中
  }
  let AL2_FCS_COEF = (1 << 7) + (1 << 6) + (1 << 5);
  let cFcs = 0;
  for (let i = 0; i < dataLength; i++) {
    cFcs ^= data[i];
    for (let j = 0; j < 8; j++) {
      if (cFcs & 1) {
        cFcs = (cFcs >>> 1) ^ AL2_FCS_COEF;
      } else {
        cFcs >>>= 1;
      }
    }
  }
  return cFcs;
  // return convertStringToFixedLength(cFcs.toString(16), 2);
};
//-----------------------------------------------------------------------//

//**Generate a HMAC ecrypted string accordding a string 根据输入的字符串生成一个HMAC加密的字符串 */
function generateHmacEcryptedString(str: string, format: string) {
  let randomNumber = Math.cbrt(new Date().getMilliseconds()).toFixed(3);
  let beforeEcryptedStr = `${randomNumber}${str}`;
  let afterEcryptedStr;
  if (format === "hex") {
    afterEcryptedStr = crypto
      .createHash("sha1")
      .update(beforeEcryptedStr)
      .digest("hex");
  } else if (format === "base64") {
    afterEcryptedStr = crypto
      .createHash("sha1")
      .update(beforeEcryptedStr)
      .digest("base64");
  }
  return afterEcryptedStr;
};

//-----------------------------------------------------------------------//

//**Generate a HMAC ecrypted string accordding a string 根据输入的字符串生成一个HMAC加密的字符串 */
function generateHmacEcryptedStringWithoutRandom(str: string, format: string) {
  let afterEcryptedStr;
  if (format === "hex") {
    afterEcryptedStr = crypto
      .createHash("sha1")
      .update(str)
      .digest("hex");
  } else if (format === "base64") {
    afterEcryptedStr = crypto
      .createHash("sha1")
      .update(str)
      .digest("base64");
  }
  return afterEcryptedStr;
};

//-----------------------------------------------------------------------//

//**Calculate BCD digit number 运算BCD码数据小数 */

function covertBCDStringToFloatNumber(bcdString: string, bcdDigitByte: string) {
  if (bcdDigitByte.length === 2) {
    let digits = parseInt(bcdDigitByte.slice(1, 2));
    let dividend = Math.pow(10, digits);
    let floatResult;
    if (bcdDigitByte.slice(0, 1) === "E" || bcdDigitByte.slice(0, 1) === "e") {
      floatResult = parseFloat((parseInt(bcdString) / dividend).toFixed(1));
    } else if (
      bcdDigitByte.slice(0, 1) === "F" ||
      bcdDigitByte.slice(0, 1) === "f"
    ) {
      floatResult = -parseFloat((parseInt(bcdString) / dividend).toFixed(1));
    }
    return floatResult;
  } else {
    throw new Error("bcdDigitByte Parameter must be a two byte string");
  }
};

//**Format Time String to "YYYY-MM-DD HH:MM:SS"格式  */
function formatTimeString(dateObj: Date, format: string) {
  let timeString;
  if (format === "YYYY-MM-DD HH:MM:SS") {
    let year = dateObj.getFullYear();
    let month = dateObj.getMonth() + 1;
    let date = dateObj.getDate();
    let hour = dateObj.getHours();
    let minutes = dateObj.getMinutes();
    let seconds = dateObj.getSeconds();
    let formatedMonth = month < 10 ? `0${month.toString()}` : month.toString();
    let formatedDate = date < 10 ? `0${date.toString()}` : date.toString();
    let formatedHour = hour < 10 ? `0${hour.toString()}` : hour.toString();
    let formatedMinutes =
      minutes < 10 ? `0${minutes.toString()}` : minutes.toString();
    let formatedSeconds =
      seconds < 10 ? `0${seconds.toString()}` : seconds.toString();
    timeString = `${year}-${formatedMonth}-${formatedDate} ${formatedHour}:${formatedMinutes}:${formatedSeconds}`;
  } else {
    //TODO MORE OPTION
    //Default to return locale string
    timeString = dateObj.toLocaleString();
  }
  return timeString;
};

/**
 * byte型转换十六进制
 * @param bytes byte型数据
 * @returns {string} HexString
 */
function bytesToHexString(bytes: number): string {
  return bytes
    .toString(16)
    .toUpperCase()
    .toString();
};

/**
 * 十六进制转换btye型
 * @param str
 * @returns {byte}
 */
function hexStringToBtye(str: string): Array<number> | null {
  let pos = 0;
  let len = str.length;
  if (len % 2 != 0) {
    return null;
  }
  len /= 2;
  let hexA = new Array();
  for (let i = 0; i < len; i++) {
    let s = str.substr(pos, 2);
    let v = parseInt(s, 16);
    hexA.push(v);
    pos += 2;
  }
  return hexA;
};
/**
 * 16进制字符串转16进制字节数组
 * @param {*} str
 * @returns {Buffer}
 */
function hexStringToHexByte(str: string): Buffer {
  //刪除空字符串，製表符等
  let withoutSpaceStr = str.replace(/\s/g, "");
  if (withoutSpaceStr.length % 2 == 0) {
    return Buffer.from(withoutSpaceStr, "hex");
  } else {
    return Buffer.from("0" + withoutSpaceStr, "hex");
  }
};
/**
 * 判断字符串是否为16进制
 * @param {*} str
 * @returns {string}
 */
function isHex(str: string): boolean {
  let regexp = /^[0-9a-fA-F]+$/;
  if (regexp.test(str)) {
    return true;
  } else {
    return false;
  }
};
function isTelNumber(str: string): boolean {
  let regexp = /^1[3|4|5|7|8|9][0-9]{9}$/;
  let regexp1 = /^((\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14})$/;
  if ((regexp.test(str) && str.length == 11) || regexp1.test(str)) {
    return true;
  } else {
    return false;
  }
};

// 以大写字母[A - Z]、 小写字母[a - z]、 数字[0 - 9]、 下滑线[_]、 减号[-] 及点号[.] 开头， 并需要重复一次至多次[+]。
// 中间必须包括 @符号。
// @之后需要连接大写字母[A - Z]、 小写字母[a - z]、 数字[0 - 9]、 下滑线[_]、 减号[-] 及点号[.]， 并需要重复一次至多次[+]。
// 结尾必须是点号[.] 连接2至4位的大小写字母[A - Za - z]
function isMail(str: string): boolean {
  let regexp = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
  if (regexp.test(str)) {
    return true;
  } else {
    return false;
  }
};
//ftp的user@
// IP形式的URL- 199.194.52.184
// 允许IP和DOMAIN（域名）
// 域名- www.
// 二级域名
// first level domain- .com or .museum
// 端口- :80
// a slash isn't required if there is no file name
// var re = new RegExp(strRegex);
function isWebsite(str: string): boolean {
  var regexp = /^((https|http|ftp|rtsp|mms)?:\/\/)+(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-z_!~*'()-]+.)*([0-9a-z][0-9a-z-]{0,61})?[0-9a-z].[a-z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+\/?)$/; //new RegExp(strRegex);
  if (regexp.test(str)) {
    return true;
  } else {
    return false;
  }
};
// let web = "http://fdsfafs";
// console.log(`${new Date().toLocaleString()}`,"isweb", basic.isWebsite(web));

function convert2F2NullOrNum(hexStr: string): number | null {
  //console.log(`${new Date().toLocaleString()}`,hexStr);
  if (hexStr === "FF" || hexStr === "ff") {
    return null;
  } else {
    return parseInt(hexStr, 16);
  }
};
function convert4F2NullOrNum(hexStr: string): number | null {
  //console.log(`${new Date().toLocaleString()}`,hexStr);
  if (hexStr === "FFFF" || hexStr === "ffff") {
    return null;
  } else {
    return parseInt(hexStr, 16);
  }
};
function convert8F2NullOrNum(hexStr: string): number | null {
  //console.log(`${new Date().toLocaleString()}`,hexStr);
  if (hexStr === "FFFFFFFF" || hexStr === "ffffffff") {
    return null;
  } else {
    return parseInt(hexStr, 16);
  }
};
/**
 * 不在当前时间范围内判断
 * @param {string}timeStr 时间 YYYY MM-DD HH:mm:ss
 * @param {number} deviationMin 偏差时间
 */
function isWithOutCurrentTime(timeStr: string, deviationSec: number) {
  try {
    //console.log(`${new Date().toLocaleString()}`,moment(timeStr).valueOf());
    let deviceTimeStamp = moment(timeStr).valueOf() / 1000;
    let currentTimeStamp = moment().unix();
    let sum = deviceTimeStamp + deviationSec;
    //console.log(`${new Date().toLocaleString()}`,`deviceTimeStamp:${deviceTimeStamp}+deviationSec:${deviationSec}=${sum}>currentTimeStamp:${currentTimeStamp}???`)
    if (sum >= currentTimeStamp) {
      // console.log(`${new Date().toLocaleString()}`,`设备状态上报时间处于正常范围内！！`);
      return false;
    } else {
      //console.log(`${new Date().toLocaleString()}`,`设备状态上报时间不在正常范围内！！`);
      return true;
    }
  } catch (error) {
    console.log(`${new Date().toLocaleString()}`, `比较时间格式不正确，格式为：YYYY-MM-DD HH:mm:ss`, error);
    return true;
  }

}

export {
  convertStringToFixedLength,
  convertUndefinedToNull,
  hex2StrAddZero,
  hexTime2Str,
  timeBuf2TimeStr,
  convertHexNumStrToWantedLengthUppercase,
  generateCurrentHexTimeString,
  generateCurrentTimeObj,
  getAHourTimestamp,
  generateOneByteSumCode,
  generateCRC8Code,
  generateHmacEcryptedString,
  generateHmacEcryptedStringWithoutRandom,
  covertBCDStringToFloatNumber,
  formatTimeString,
  bytesToHexString,
  hexStringToBtye,
  hexStringToHexByte,
  isHex,
  isTelNumber,
  isMail,
  isWebsite,
  convert2F2NullOrNum,
  convert4F2NullOrNum,
  convert8F2NullOrNum,
  isWithOutCurrentTime,
  nanNumber2Null
}